# -*- coding: UTF-8 -*-
import requests
import json, random
import logging
import base64
import urllib.parse
from Cryptodome import Random
from Cryptodome.PublicKey import RSA
from Cryptodome.Hash import SHA1
from Cryptodome.Cipher import PKCS1_v1_5 as CPKCS8  # 加密解密模块
from Cryptodome.Signature import pkcs1_15  # 加签验签模块
from django.conf import settings

requests.packages.urllib3.disable_warnings(
    requests.packages.urllib3.exceptions.InsecureRequestWarning
)

logger = logging.getLogger("allinpay")
logger.debug("初始化 Allinpay API")

"""
DEBUG 细节信息，仅当诊断问题时适用。
INFO 确认程序按预期运行
WARNING 表明有已经或即将发生的意外（例如：磁盘空间不足）。程序仍按预期进行
ERROR 由于严重的问题，程序的某些功能已经不能正常执行
CRITICAL 严重的错误，表明程序已不能继续执行
"""

# 接口列表
Basics = {
    "pays": "/pay",
    "scan": "/scanqrpay",
    "cancel": "/cancel",
    "refund": "/refund",
    "query": "/query",
}


def to_format_url(u):
    """格式化url地址"""
    if u[-1] == "/":
        return u[:-1]
    else:
        return u


def getRandom(l=8):
    """创建订单随机码"""
    s = ""
    for i in range(l):
        s += str(random.choice(range(9)))
    return s


def dict_to_str(di):
    """排序"""
    s = ""
    di = [(k, di[k]) for k in sorted(di.keys())]
    for k, v in di:
        if not v:
            continue
        s += f"{k}={v}&"
    return s[:-1]


class Urlchuli:
    """Url处理类，需要传入两个实参：Urlchuli('实参','编码类型')，默认utf-8
    url编码方法：url_bm() url解码方法：url_jm()"""

    def __init__(self, can, mazhi="utf-8"):
        self.can = can
        self.mazhi = mazhi

    def url_bm(self):
        """url_bm() 将传入的中文实参转为Urlencode编码"""
        quma = str(self.can).encode(self.mazhi)
        bianma = urllib.parse.quote(quma)
        return bianma

    def url_jm(self):
        """url_jm() 将传入的url进行解码成中文"""
        quma = str(self.can)
        jiema = urllib.parse.unquote(quma, self.mazhi)
        return jiema


class allInPayApi(requests.Session):
    """通联请求对象API"""

    def has_conf(self):
        try:
            with open(settings.CONF / "allinpay.json", "r") as fs:
                Conf = json.loads(fs.read())
        except BaseException as err:
            Conf = None
        return Conf

    def has_headers(self):
        return self.headers

    def RSA_design(self, secret, data, c=False):
        """RSA 公钥验签"""
        try:
            signature = data.pop("sign")
            s = dict_to_str(data)
            key = RSA.importKey(base64.b64decode(secret))
            hash_obj = SHA1.new(s.encode("utf-8"))
            pkcs1_15.new(key).verify(hash_obj, base64.b64decode(signature))
            if c:
                del data["appid"]
                del data["cusid"]
                del data["randomstr"]
                del data["retcode"]
                del data["trxstatus"]
            return data
        except BaseException as err:
            logger.warning(
                {
                    "reqsn": data.get("reqsn"),
                    "order": data.get("order"),
                    "message": "验签失败.",
                }
            )
            return None

    def RSA_ensign(self, secret, data):
        """私钥加签"""
        try:
            s = dict_to_str(data)
            bprivate = base64.b64decode(secret)
            key = RSA.importKey(bprivate)
            hash_obj = SHA1.new(s.encode("utf-8"))
            signature = pkcs1_15.new(key).sign(hash_obj)
            sing = base64.b64encode(signature).decode("utf-8")
            sing = Urlchuli(sing, "gbk").url_bm().replace("/", f"%2f")
            return sing
        except BaseException as err:
            logger.warning(
                {
                    "reqsn": data.get("reqsn"),
                    "order": data.get("order"),
                    "message": "加签失败.",
                }
            )
            return None

    def to_format_data(self, act, headers=None, data=None):
        """预处理 格式化请求数据"""
        conf = self.has_conf()
        if not conf or not conf.get("status"):
            return None
        # print(conf)
        # 处理 url地址
        url = f'{to_format_url(conf.get("interface"))}{Basics.get(act)}'
        # print("url:", url)
        # 处理 header
        h = headers or self.has_headers()
        h["Content-Type"] = "application/x-www-form-urlencoded"
        # print("header:", h)
        # 处理 data参数
        # 加入消息通知字段
        if act in ["pays", "scan"]:
            data["notify_url"] = conf.get("notify")
        # 加入商户ID字段
        data["cusid"] = conf.get("shopid")
        # 加入商户appid字段
        data["appid"] = conf.get("appid")
        # 加入随机码字段
        data["randomstr"] = getRandom()
        # 加入签名方法
        data["signtype"] = "RSA"
        # 加入签名值
        sign = self.RSA_ensign(conf.get("rsaprivate"), data)
        if not sign:
            return None
        data["sign"] = sign
        # print("data:", data)
        # 同步请求
        res = self.do_post(
            url,
            headers=h,
            data=dict_to_str(data),
        )
        if not res:
            return None
        # print("res:", res)
        # 验证签名
        r = self.RSA_design(conf.get("bnkpublic"), res, c=True)
        return r

    def do_post(self, url="", headers=None, params=None, data=None, verify=False):
        try:
            r = self.post(url, headers=headers, params=params, data=data, verify=verify)
            return r.json()
        except BaseException as err:
            logger.warning(f"{err}")
            return None


# 返回对象
# r = {
#     "appid": "00264649",
#     "cusid": "56213404816ABTA",
#     "payinfo": "https://syb.allinpay.com/apiweb/h5unionpay/native?key=pgVYmc533uiO%2Bgt6Qc8v%2BWcR",
#     "randomstr": "831780836698",
#     "reqsn": "TY20230407144403524",
#     "retcode": "SUCCESS",
#     "sign": "QP/1SEoS7WS4ykjaQDjnFcxDja6bSFk4FT/hKoP7GSSqbcP9cxtC8EzI/6omVDBt77s6U2oWfPr4LpIjwOLwDvDjX3lXvLxjCAUIK63iZ32OBoe020s3WiUYYJEBBnFYqBpE05hs1xgHt19m1QoSLMvBF5YRcpoFYy4V9t02Szw=",
#     "trxid": "230407111558458186",
#     "trxstatus": "0000",
# }
# 实例化对象
PAYAPI = allInPayApi()
